const Webpack = require('webpack') // 用于访问 webpack 内置插件
const CopyWebpackPlugin = require('copy-webpack-plugin') // 引入文件赋值插件
const HtmlWebpackPlugin = require('html-webpack-plugin') // 引入生成 html 并自动引入 bundle 插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 引入 css 提取插件
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin') // 引入 css 压缩插件
const EslintWebpackPlugin = require('eslint-webpack-plugin') // 引入 eslint 格式化插件
const CompressionWebpackPlugin = require('compression-webpack-plugin') // 引入 gzip 打包压缩文件插件 准备资产的压缩版本以使用 Content-Encoding 为它们提供服务。
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin // 引入 bundle 分析插件
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin') // 引入友好错误提示插件

const HappyPack = require('happypack') // 引入多线程打包 默认 webpack 打包为单线程 构建速度慢
const { resolve, join } = require('path')

// 路径方法定义
const pathResolve = file => resolve(__dirname, file)
const pathJoin = file => join(__dirname, file)

// 是否为开发环境
const DEV_MODE = process.env.NODE_ENV !== 'production'
// 处理 css 样式提取 推荐 production 环境的构建将 CSS 从你的 bundle 中分离出来，这样可以使用 CSS/JS 文件的并行加载
const stylesHandler = DEV_MODE
  ? 'style-loader'
  : MiniCssExtractPlugin.loader

// loader 处理规则
const rules = [
  /* 处理 html 中直接使用 img 标签 src 加载图片不能被 webpack 解析打包问题 */
  {
    test: /\.htm(l)?$/i,
    loader: 'html-withimg-loader',
    exclude: /node_modules/ // 排除 node_modules 目录查找
  },
  /* 加载与转换 styles 样式 */
  // region
  // css 处理
  {
    test: /\.css$/,
    use: [
      // 开发环境将 JS 字符串生成为 style 节点 生产环境 css 分离
      stylesHandler,
      // 将 CSS 转化成 CommonJS 模块 解析css文件中的@import依赖关系
      'css-loader',
      // 使用 PostCSS 处理 CSS
      {
        loader: 'postcss-loader',
        options: {
          postcssOptions: {
            plugins: [
              ['postcss-preset-env'] // PostCSS Preset Env 让您可以将现代 CSS 转换为大多数浏览器可以理解的内容，根据您的目标浏览器或运行时环境确定您需要的 polyfill
            ]
          }
        }
      }
    ]
  },
  // sass 处理
  {
    // text: /\.(sa|sc)ss$/i,
    test: /\.s[ac]ss$/i, // 正则匹配符合规则的文件 与 resource 配置冲突
    // include: pathResolve('src/assets/styles'), // 准确指定路径可以提高性能 与 resource 配置冲突
    exclude: /node_modules/, // 排除 node_modules 目录查找 与 resource 配置冲突
    use: [
      // 开发环境将 JS 字符串生成为 style 节点 生产环境 css 分离
      stylesHandler,
      // 将 CSS 转化成 CommonJS 模块
      'css-loader',
      // 使用 PostCSS 处理 CSS
      {
        loader: 'postcss-loader',
        options: {
          postcssOptions: {
            plugins: [
              ['postcss-preset-env'] // PostCSS Preset Env 让您可以将现代 CSS 转换为大多数浏览器可以理解的内容，根据您的目标浏览器或运行时环境确定您需要的 polyfill
            ]
          }
        }
      },
      // 将 Sass 文件编译为 CSS
      'sass-loader'
    ]
  },
  // less 处理
  {
    test: /\.less$/i,
    // include: pathResolve('src/assets/styles'), // 准确指定路径可以提高性能
    exclude: /node_modules/, // 排除 node_modules 目录查找
    use: [
      // 开发环境将 JS 字符串生成为 style 节点 生产环境 css 分离
      stylesHandler,
      // 将 CSS 转化成 CommonJS 模块
      'css-loader',
      // 使用 PostCSS 处理 CSS
      {
        loader: 'postcss-loader',
        options: {
          postcssOptions: {
            plugins: [
              ['postcss-preset-env'] // PostCSS Preset Env 让您可以将现代 CSS 转换为大多数浏览器可以理解的内容，根据您的目标浏览器或运行时环境确定您需要的 polyfill
            ]
          }
        }
      },
      // 将 Less 文件编译为 CSS
      'less-loader'
    ]
  },
  // stylus 处理
  {
    test: /\.styl$/i,
    // include: pathResolve('src/assets/styles'), // 准确指定路径可以提高性能
    exclude: /node_modules/, // 排除 node_modules 目录查找
    use: [
      // 开发环境将 JS 字符串生成为 style 节点 生产环境 css 分离
      stylesHandler,
      // 将 CSS 转化成 CommonJS 模块
      'css-loader',
      // 使用 PostCSS 处理 CSS
      {
        loader: 'postcss-loader',
        options: {
          postcssOptions: {
            plugins: [
              ['postcss-preset-env'] // PostCSS Preset Env 让您可以将现代 CSS 转换为大多数浏览器可以理解的内容，根据您的目标浏览器或运行时环境确定您需要的 polyfill
            ]
          }
        }
      },
      // 将 Stylus 文件编译为 CSS
      'stylus-loader'
    ]
  },
  // endregion
  /* 加载与转换 scripts 脚本 */
  // region
  // js 处理
  /* {
    test: /\.js(x)?$/i,
    // include: pathResolve('src/assets/scripts'),
    exclude: /node_modules/, // 排除 node_modules 目录查找
    use: [
      {
        loader: 'babel-loader', // 在企业开发中为了兼容一些低级版本的浏览器, 我们需要将 ES6+ 高级语法转换为 ES5 兼容浏览器能识别的低级语法
        // options 配置或根目录 .babelrc 配置
        options: {
          presets: ['@babel/preset-env'], // 使用预设配置
          plugins: ['@babel/plugin-transform-runtime'], // Babel 对一些公共方法使用了非常小的辅助代码，比如 _extend。默认情况下会被添加到每一个需要它的文件中。你可以引入 Babel runtime 作为一个独立模块，来避免重复引入。
          // 排除不应参与转码的库
          exclude: [
            // core-js 和 webpack/buildin 如果被 Babel 转码会发生错误
            /node_modules[\\/]core-js/,
            /node_modules[\\/]webpack[\\/]buildin/
          ]
        }
      }
    ]
  }, */
  {
    test: /\.js(x)?$/i,
    // include: pathResolve('src/assets/scripts'),
    exclude: /node_modules/, // 排除 node_modules 目录查找
    use: 'happypack/loader?id=js'
  },
  // ts 处理
  {
    test: /\.ts(x)?$/i,
    // include: pathResolve('src/assets/scripts'),
    exclude: /node_modules/, // 排除 node_modules 目录查找
    use: [
      {
        loader: 'ts-loader',
        options: {
          // transpileOnly: true, // 关闭类型检查
        }
      }
    ]
  },
  // endregion
  /* 加载与转换 assets 资源 */
  // region
  /* 加载与转换 images 图像(默认会处理) */
  {
    // text.js: /\.(webp|png|svg|jpg|jpeg|gif)$/i,
    test: /\.(webp|png|svg|jpe?g|gif)$/i,
    /* include: [
      pathResolve('src/assets/images'),
      pathResolve('src/assets/icons')
    ], // 准确指定路径可以提高性能 */
    exclude: /node_modules/, // 排除 node_modules 目录查找
    /*
    asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
    asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
    asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
    asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader，并且配置资源体积限制实现。
    javascript/auto 当在 webpack 5 中使用旧的 assets loader（如 file-loader/url-loader/raw-loader 等）和 asset 模块时，你可能想停止当前 asset 模块的处理，并再次启动处理，解决 asset 重复
    */
    type: 'asset',
    parser: { // 解析选项对象。所有应用的解析选项都将合并
      dataUrlCondition: { // 如果一个模块源码大小小于 maxSize，那么模块会被作为一个 Base64 编码的字符串注入到包中， 否则模块文件会被生成到输出的目标目录中。
        maxSize: 10 * 1024 // 小于 10kb 自动转换为 dataUrl 格式 默认为 8kb
      }
    },
    generator: { // 生成器
      // 指定生成该资源的文件名
      filename: 'assets/images/[name].[contenthash][ext]'
    },
    use: [
      /**
       * 图片压缩 yarn 与 npm 安装失败
       * 使用 cnpm 安装
       * npm install cnpm -g --registry=https://registry.npm.taobao.org
       * cnpm install --save-dev  image-webpack-loader
       */
      /* {
        loader: 'image-webpack-loader',
        options: {
          mozjpeg: {
            progressive: true,
            quality: 65
          },
          optipng: {
            enabled: false
          },
          pngquant: {
            quality: [0.65, 0.90],
            speed: 4
          },
          gifsicle: {
            interlaced: false
          },
          webp: {
            quality: 75
          }
        }
      } */
    ]
  },
  /* 加载与转换 fonts 字体(默认会处理) */
  {
    // text.js: /\.(woff|woff2|eot|ttf|otf)$/i,
    test: /\.(woff2?|eot|[to]tf)$/i,
    // include: pathResolve('src/assets/fonts'),
    exclude: /node_modules/,
    type: 'asset/resource',
    generator: { // 生成器
      // 指定生成该资源的文件名
      filename: 'assets/fonts/[name].[contenthash][ext]'
    }
  },
  /* 统一处理其它资源 */
  {
    test: /\.(mp3|mp4|avi)$/i,
    // include: pathResolve('src/assets'),
    exclude: /node_modules/,
    type: 'asset/resource',
    generator: { // 生成器
      // 指定生成该资源的文件名
      filename: 'assets/other/[name].[contenthash][ext]'
    }
  }
  /* 加载 JSON(已内置)、CSV、TSV 和 XML 数据 */
  /* {
    // text.js: /\.(csv|tsv)$/i,
    test: /\.[ct]sv$/i,
    include: pathResolve('src/assets/datas'),
    exclude: /node_modules/,
    use: ['csv-loader']
  },
  {
    test: /\.xml$/i,
    include: pathResolve('src/assets/datas'),
    exclude: /node_modules/,
    use: ['xml-loader']
  } */
  // endregion
]
// 插件
const plugins = [
  /* 第三方插件 S */
  // 使用交互式可缩放树形图可视化 webpack 输出文件的大小。
  new BundleAnalyzerPlugin({
    openAnalyzer: false // 不自动打开
  }),
  // 使用友好错误提示插件cls
  new FriendlyErrorsWebpackPlugin(),
  // html-webpack-plugin 创建了一个全新的 index.html 文件，所有的 bundle 相关依赖会自动添加到 html 中。
  /* new HtmlWebpackPlugin({
    template: pathJoin('src/index.html'), // 指定加载模版
    filename: 'index.html', // 指定生成文件名
    chunks: ['index'] // 指定 chunks 防止所有页面的 js 都注入到 html chunk 名称对应 entry 入口
    // title: 'Webpack Demo', // 设置 html 标题
  }),
  new HtmlWebpackPlugin({
    template: pathJoin('src/index.html'), // 指定加载模版
    filename: 'about.html', // 指定生成文件名
    chunks: ['about'] // 指定 chunks 防止所有页面的 js 都注入到 html
  }), */
  // copy-webpack-plugin 将已存在的单个文件或整个目录复制到构建目录。
  new CopyWebpackPlugin({
    patterns: [
      { from: 'public/', to: '.' } // 将 public 目录下的内容拷贝到 dist 目录下
    ]
  }),
  // mini-css-extract-plugin 用于生产环境 css 分离
  /* new MiniCssExtractPlugin({
    filename: 'assets/styles/[name].[contenthash].css'
  }), */
  // eslint-webpack-plugin 使用 eslint 来查找和修复 JavaScript 代码中的问题
  /* new EslintWebpackPlugin({
    extensions: ['ts', 'tsx', 'js', 'jsx'], // 指定需要检查的扩展名
    exclude: 'node_modules', // 指定需要排除的文件及目录。必须是相对于 options.context 的相对路径。
    fix: true // 启用 ESLint 自动修复特性。小心: 该选项会修改源文件。
  }), */
  // compression-webpack-plugin 用于 gzip 打包压缩文件
  // new CompressionWebpackPlugin(), // 生成的压缩文件是不能直接使用的，需要服务端给浏览器发送"Content-Encoding=gzip"，让浏览器通过gzip编码来解析资源
  // happypack 多线程打包
  new HappyPack({
    id: 'js',
    // threads: 3,
    use: [{
      test: /\.js(x)?$/i,
      loader: 'babel-loader',
      options: {
        presets: ['@babel/preset-env'],
        plugins: ['@babel/plugin-transform-runtime']
      }
    }]
  }),
  /* 第三方插件 E */

  /* webpack 内置插件 S */
  // AutomaticPrefetchPlugin 会观察之前编译的 所有模块 的变化，以改进增量构建的时间。
  new Webpack.AutomaticPrefetchPlugin(), // 可能有性能上的好处，也可能没有，因为增量构建时间非常快。
  // BannerPlugin 为每个 chunk 文件头部添加 banner。
  /* new webpack.BannerPlugin({
    banner: 'Made in DefChigga'
  }), */
  // DefinePlugin 允许在 编译时 将你代码中的变量替换为其他值或表达式。
  /* new Webpack.DefinePlugin({
    DEV_MODE: process.env.NODE_ENV !== 'production', // 值不是字符串会被转为字符串
    PI: Math.PI // 值不是字符串会被转为字符串
  }), */
  // ProgressPlugin 用于自定义编译过程中的进度报告
  new Webpack.ProgressPlugin()
  // ProvidePlugin 定义包导入的全局变量 如果遇到用到 _ 变量的模块实例，那请你将 lodash package 引入进来，并将其提供给需要用到它的模块。
  /* new Webpack.ProvidePlugin({
    _: 'lodash', // 项目中可以直接使用 _ 而不用手动导入 lodash 库
    join: ['lodash', 'join'] // 项目中可以直接使用 lodash 下的 join 方法 有利于 tree shaking
  }) */
  /* webpack 内置插件 E */
]

// webpack 配置
let config = {
  mode: DEV_MODE ? 'development' : 'production', // 指定打包模式 "production" | "development" | "none"
  devtool: DEV_MODE ? 'eval-cheap-module-source-map' : false, // 此选项控制是否生成，以及如何生成 source map。 通过为浏览器调试工具提供极其详细的源映射的元信息来增强调试能力，但会牺牲构建速度。
  // context: pathResolve(''), // 基础目录，绝对路径，用于从配置中解析入口点(entry point)和 加载器(loader)。默认使用 Node.js 进程的当前工作目录，但是推荐在配置中传入一个值。这使得你的配置独立于 CWD(current working directory, 当前工作目录)。
  entry: {
    // 手动代码分离（多页面 chunk）
    index: './src/index.js',
    about: './src/about.js'
  }, // 指定入口文件 string | object | array 默认路径为字符串 ./src/index.js
  /* 指定输出配置 */
  output: {
    path: pathResolve('dist'), // 输出路径 output 目录对应一个绝对路径 pathJoin(process.cwd(), 'dist')
    filename: DEV_MODE ? '[name].bundle.js' : 'assets/scripts/[name].[contenthash].bundle.js', // 输出 bundle 的名称 [name] 表示entry配置下的main(键名) [contenthash] substitution 将根据资源内容创建出唯一 hash。当资源内容发生变化时，[contenthash] 也会发生变化。
    // [file] 等价于 [path][base]。[base] 等价于 [name][ext]。完整的路径为 [path][name][ext][query][fragment] 或 [path][base][query][fragment] 或 [file][query][fragment]。
    assetModuleFilename: DEV_MODE ? '[base]' : '[path][name].[contenthash][ext]', //  定义全局 Asset Modules 资源输出文件名，会被资源模块下的 generator 下的 filename 替换
    hashDigestLength: 8, // [hash]，[contenthash] 或者 [chunkhash] 的长度可以使用 [hash:8]（默认为 20）来指定。或者，通过指定output.hashDigestLength 在全局配置长度。
    clean: true // 每次构建前清理 output 打包目录 dist 取代 CleanWebpackPlugin 插件使用
  },
  /* webpack-dev-server 开发环境配置 - https://webpack.docschina.org/configuration/dev-server/#devserver */
  devServer: { // 提供了一个基本的 web server，并且具有 live reloading(实时重新加载) 功能  devServer 只能解决开发阶段的跨域问题 scripts: webpack serve
    host: '0.0.0.0', // 指定要使用的 host。0.0.0.0 可以让服务器被外部访问 同一网段下外部可以通过本机 ip 地址访问到该服务
    port: 80, // 指定监听请求的端口号 默认 8080
    static: pathJoin('public'), // 该配置项允许配置从目录提供静态文件的选项（默认是 'public' 文件夹）。推荐使用绝对路径 将其设置为 false 以禁用 boolean | string | array | object
    compress: true, // 是否启用 gzip 压缩 public 目录当中的所有内容
    // open: true, // 是否在服务器启动后打开默认浏览器 open: ['/my-page'] 在浏览器中打开指定页面 字符串打开单个页面 数组打开多个页面
    // historyApiFallback: true, // 使用 HTML5 History API 时，可能必须提供 index.html 页面来代替任何 404 响应。设置为 true 来启用它
    hot: true, // 是否启用热模块替换 从 webpack-dev-server v4.0.0 开始，热模块替换是默认开启的 不需要手动添加 webpack.HotModuleReplacementPlugin 插件
    https: false, // 开发服务器是否使用 https 协议提供服务 默认通过 HTTP 提供服务
    // 后端开发服务器的代理
    proxy: {
      '/api': { // 对 /api/users 的请求会将请求代理到 http://localhost:3000/api/users
        target: 'http://localhost:3000', // 代理地址
        changeOrigin: true, // 域名跨域 默认情况下，代理时会保留主机头的来源，可以将 changeOrigin 设置为 true 以覆盖此行为
        secure: false, // https 跨域 是否验证SSL Certs 接受在 HTTPS 上运行且证书无效的后端服务器
        server: 'http', // 允许设置服务器和配置项（默认为 'http'） 'http' | 'https' | 'spdy' string object
        ws: false, // 是否代理 websocket
        pathRewrite: { '^/api': '' } // 如果不希望传递/api，则需要重写路径
      }
    },
    // 客户端配置
    client: {
      logging: 'info', // 设置日志级别 'log' | 'info' | 'warn' | 'error' | 'none' | 'verbose'
      // overlay: true, // 当出现编译错误或警告时，在浏览器中显示全屏覆盖
      overlay: { // 只显示错误信息
        errors: true,
        warnings: false
      }
      // progress: true // 在浏览器控制台中以百分比打印显示编译进度
    }
  },
  // watch: true, // 启用 Watch 模式。这意味着在初始构建之后，webpack 将继续监听任何已解析文件的更改。webpack-dev-server 和 webpack-dev-middleware 里 Watch 模式默认开启
  watchOptions: { // 一组用来定制 watch 模式的选项
    // 对于某些系统，监听大量文件会导致大量的 CPU 或内存占用。
    ignored: /node_modules/ // 可以使用正则排除像 node_modules 如此庞大的文件夹
    // ignored: '**/node_modules', // 还可以使用 glob 模式排除像 node_modules 如此庞大的文件夹
    // poll: 1000, // 每隔 1S 检查一次变动。通过传递 true 开启 polling，或者指定毫秒为单位进行轮询。如果监听没生效，试试这个选项吧。这会对 VirtualBox、WSL、Containers 或者 Docker 有所帮助。在这些情况下，使用轮询间隔并忽略 /node_modules/ 这样的大文件夹，以保持最小的 CPU 使用率。
    // stdin: true, // 当 stdin 流结束时停止监听。
    // aggregateTimeout: 300, // 防抖, 和函数防抖一样, 改变过程中不重新打包, 只有改变完成指定时间后才打包
  },
  /* 模块解析 */
  resolve: {
    // 设置路径别名
    alias: {
      node_modules: false, // 忽略 node_modules 模块
      public: pathResolve('public'),
      src: pathResolve('src'),
      assets: pathResolve('src/assets')
    },
    // 设置后缀名 尝试按顺序解析这些后缀名 能够使用户在引入模块时不带扩展
    extensions: ['.ts', '.tsx', '...'] // 使用 '...' 访问默认拓展名 '.js', '.jsx', '.json'
    // mainFiles: ['index'], // 解析目录时要使用的文件名
  },
  /* 模块 */
  module: {
    /* loader 加载器处理 */
    rules
  },
  /* plugins 插件处理 */
  plugins,
  /* 优化配置 */
  optimization: {
    // runtimeChunk: 'single', // 选项将 runtime 代码拆分为一个单独的 chunk, 在一个 HTML 页面上使用多个入口时配置
    runtimeChunk: DEV_MODE, // 最小化 entry chunk 确保在生成 entry chunk 时，尽量减少其体积以提高性能
    splitChunks: { // SplitChunksPlugin 插件可以将公共的依赖模块提取到已有的入口 chunk 中，或者提取到一个新生成的 chunk
      chunks: 'all' // 代码分离
    },
    removeAvailableModules: DEV_MODE, // 生产环境设置为 false 以获得额外的构建性能 如果模块已经包含在所有父级模块中，告知 webpack 从 chunk 中检测出这些模块，或移除这些模块
    minimizer: [ // 生产环境开启代码压缩优化 配置会覆盖现有的 minimizer
      /* // 在 webpack@5 中，你可以使用 `...` 语法来扩展现有的 minimizer（即 `terser-webpack-plugin`）
      '...',
      // css-minimizer-webpack-plugin 使用 cssnano 优化和压缩 CSS。就像 optimize-css-assets-webpack-plugin 一样，但在 source maps 和 assets 中使用查询字符串会更加准确，支持缓存和并发模式下运行。
      new CssMinimizerWebpackPlugin() */
    ]
    // minimize: true, // 开发环境开启代码压缩
  }
  /* 性能 */
  /* performance: {
    // 资源过滤 以下示例将只给出 .css 与 .js 文件的性能提示
    /!* assetFilter: function (assetFilename) {
      return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
    }, *!/
    // maxAssetSize: 250000, // 限制最大资源大小。默认为 250000 1024 * 244 244KiB
    // hints: DEV_MODE ? 'warning' : 'error' // 打开/关闭提示 资源体积超过设置值时告诉 webpack 抛出一个错误或警告。此属性默认设置为 "warning"。 string = 'warning': 'error' | 'warning' boolean: false
  } */
}

// 导入自定义配置
const cwd = process.cwd()

// 用 def.config.js 传入的配置替换 webpack 默认配置
try {
  const loadConfig = require(`${cwd}/def.config.js`)
  config = { ...config, ...loadConfig }
} catch (e) {
  throw new Error(e)
}

// module.exports = {}
// 接收环境变量 return 返回对象配置
module.exports = (env, argv) => {
  // 根据 entry 对象配置 生成对应的多页面
  const isObject = Array.prototype.toString.apply(config.entry).slice(8, -1) === 'Object'
  if (!isObject) throw new Error('config.entry 需要传入一个对象')
  Object.keys(config.entry).forEach(key => {
    config.plugins.push(
      new HtmlWebpackPlugin({
        template: pathJoin('src/index.html'), // 指定加载模版
        filename: key + '.html', // 指定生成文件名
        chunks: [key, 'vendors~' + key] // 指定 chunks 防止所有页面的 js 都注入到 html
      })
    )
  })
  // 开发环境
  if (DEV_MODE) {
    // config.mode = 'development'
    // config.devtool = 'eval-cheap-module-source-map'
    config.plugins.push(
      new EslintWebpackPlugin({
        extensions: ['ts', 'tsx', 'js', 'jsx'], // 指定需要检查的扩展名
        exclude: 'node_modules', // 指定需要排除的文件及目录。必须是相对于 options.context 的相对路径。
        fix: true // 启用 ESLint 自动修复特性。小心: 该选项会修改源文件。
      })
    )
    return config
  }
  // 生产环境
  // config.mode = 'production'
  // config.devtool = false
  const imagesLoader = config.module.rules.find(item => item.type === 'asset')
  imagesLoader.use.push({
    loader: 'image-webpack-loader',
    options: {
      mozjpeg: {
        progressive: true,
        quality: 65
      },
      optipng: {
        enabled: false
      },
      pngquant: {
        quality: [0.65, 0.90],
        speed: 4
      },
      gifsicle: {
        interlaced: false
      },
      webp: {
        quality: 75
      }
    }
  })
  config.plugins.push(
    new MiniCssExtractPlugin({
      filename: 'assets/styles/[name].[contenthash].css'
    }),
    new CompressionWebpackPlugin({
      // filename: '[path][base].gz', // [file] 会被替换成原始资源。[path] 会被替换成原始资源的路径， [query] 会被替换成查询字符串
      algorithm: 'gzip', // 压缩方式 默认为 gzip
      test: /\.(js|css)$/, // 所有匹配该正则的资源都会被处理。默认值是全部资源。
      // threshold: 10240, // 只有大小大于该值的资源会被处理。单位是 bytes。默认值是 0。
      minRatio: 0.8 // 只有压缩率小于这个值的资源才会被处理。默认值是 0.8。
    })
  )
  config.optimization.minimizer.push(
    '...',
    new CssMinimizerWebpackPlugin()
  )
  return config
}
